`timescale 10ns/1ns
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    16:29:34 05/28/2012 
// Design Name: 
// Module Name:    USB 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
//Three always blocks code style - registered outputs
module USB(
		input      CLK_USB, nRST,
		output     [1:0] USB_FIFOADDR,
		output reg nUSB_PKTEND, nUSB_SLWR, nUSB_SLOE, nUSB_SLRD,
		input      nUSB_FULL,
		input      nUSB_EMPTY,
		inout [7:0] USB_FD,
		
		input [7:0] FIFO_DOUT,
		output reg  FIFO_RD_EN,
		input	    FIFO_EMPTY,

		output reg execute,
		output     [39:0] command
    );
parameter
	READIDLE   = 3'b000,
	READOPCODE = 3'b001,
	READWAIT   = 3'b010,
	READLONG   = 3'b100;

parameter
	WRITEIDLE   = 2'b00,
	WRITEDATA   = 2'b01,
	WRITEWAIT   = 2'b10;

parameter
    READADDR   = 1'b0,
    WRITEADDR  = 1'b1;
//=============================================================================
reg [2:0] read_state, next_read_state;
reg [1:0] write_state, next_write_state;
reg [7:0] cmd, next_cmd;
reg [31:0] cmd_param, next_cmd_param;
reg usb_fifo_addr_1, next_usb_fifo_addr_1;
reg next_nUSB_PKTEND;
reg next_nUSB_SLWR;
reg next_nUSB_SLOE;
reg next_nUSB_SLRD;
reg next_execute = 0;
reg [7:0] usb_fd_reg, next_usb_fd_reg;
reg next_FIFO_RD_EN;

//=============================================================================
assign command = {cmd, cmd_param};
assign USB_FIFOADDR[0] = 0;
assign USB_FIFOADDR[1] = usb_fifo_addr_1;
assign USB_FD = nUSB_SLOE ? usb_fd_reg : 8'bzzzzzzzz;
//=============================================================================
always @(posedge CLK_USB or negedge nRST) begin
	if(!nRST) begin
		read_state <= READIDLE;
		write_state<= WRITEIDLE;
		usb_fifo_addr_1 <= READADDR;
		nUSB_PKTEND <= 1;
		nUSB_SLWR <= 1;
		nUSB_SLOE <= 1;
		nUSB_SLRD <= 1;
		usb_fd_reg <= 8'h00;
		FIFO_RD_EN <= 0;
		execute <= 0;
		cmd <= 8'h00;
		cmd_param <= 32'h00000000;
	end else begin
		read_state <= next_read_state;
		write_state <= next_write_state;
		usb_fifo_addr_1 <= next_usb_fifo_addr_1;
		nUSB_PKTEND <= next_nUSB_PKTEND;
		nUSB_SLWR <= next_nUSB_SLWR;
		nUSB_SLOE <= next_nUSB_SLOE;
		nUSB_SLRD <= next_nUSB_SLRD;
		usb_fd_reg <= next_usb_fd_reg;
		FIFO_RD_EN <= next_FIFO_RD_EN;
		execute <= next_execute;
		cmd <= next_cmd;
		cmd_param <= next_cmd_param;
	end
end

always @(*) begin
	next_read_state = read_state;
	next_usb_fifo_addr_1 = usb_fifo_addr_1;
	next_nUSB_SLOE = nUSB_SLOE;
	next_nUSB_SLRD = 1;
	next_execute = 0;
	next_cmd = cmd;
	next_cmd_param = cmd_param;

	//Read data from usb
	case (read_state)
		READIDLE: begin
			if(nUSB_EMPTY) begin
				next_usb_fifo_addr_1 = READADDR;
				next_nUSB_SLOE = 0;
				next_read_state  = READOPCODE;
			end else begin
				next_nUSB_SLOE = 1;
			end
		end
		READOPCODE:	begin
			next_nUSB_SLRD = 0;
			next_cmd = USB_FD;
			next_read_state = READWAIT;
		end
		READWAIT: begin
			next_read_state = READLONG;
		end
		READLONG: begin
			if(nUSB_EMPTY) begin
				next_nUSB_SLRD = 0;
				next_cmd_param = {cmd_param[23:0],USB_FD};
				next_read_state  = READWAIT;
			end else begin
				next_execute = 1;
				next_read_state  = READIDLE;
			end
		end
	endcase
	
	next_FIFO_RD_EN = 0;
	next_usb_fd_reg = usb_fd_reg;
	next_write_state = write_state;
	next_nUSB_PKTEND = 1;
	next_nUSB_SLWR = 1;
	
	case (write_state)
		WRITEIDLE: begin
			if(nUSB_FULL && (!FIFO_EMPTY)) begin
				if(!FIFO_RD_EN) begin
					next_FIFO_RD_EN = 1;
					next_usb_fifo_addr_1 = WRITEADDR;
				end else begin
					next_write_state = WRITEDATA;
				end
			end
		end
		WRITEDATA: begin
			next_usb_fd_reg = FIFO_DOUT;
			next_nUSB_SLWR = 0;
			next_write_state = WRITEWAIT;
		end
		WRITEWAIT: begin
			if(FIFO_EMPTY) begin
				next_nUSB_PKTEND = 0;
			end
			next_write_state = WRITEIDLE;
		end
	endcase
end

endmodule
